// COCO关键点连接定义
const POSE_CONNECTIONS = [
    // 头部
    [0, 1], [0, 2], [1, 3], [2, 4], // 鼻子到眼睛和耳朵
    // 躯干
    [5, 6], [5, 11], [6, 12], [11, 12], // 肩膀和髋部
    // 左臂
    [5, 7], [7, 9], // 左肩到左肘到左腕
    // 右臂
    [6, 8], [8, 10], // 右肩到右肘到右腕
    // 左腿
    [11, 13], [13, 15], // 左髋到左膝到左踝
    // 右腿
    [12, 14], [14, 16], // 右髋到右膝到右踝
];

// 关键点名称
const KEYPOINT_NAMES = [
    '鼻子', '左眼', '右眼', '左耳', '右耳',
    '左肩', '右肩', '左肘', '右肘', '左腕', '右腕',
    '左髋', '右髋', '左膝', '右膝', '左踝', '右踝'
];

// 全局变量保存当前person_id和task_id
let personID = 'testuser';
let taskID = 'testtask';

class PoseVisualizer {
    constructor() {
        console.log('PoseVisualizer构造函数开始，全局变量:', { personID, taskID });
        
        this.canvas = document.getElementById('poseCanvas');
        this.ctx = this.canvas.getContext('2d');
        this.currentPose = null;
        this.referencePose = null; // 参考骨骼数据
        this.isCalibrated = false;
        this.isExamMode = false; // 考试模式
        this.frameCount = 0;
        this.showReferenceSkeleton = true;
        this.showCurrentSkeleton = true;
        this.allPoseData = []; // 存储所有导入的数据
        this.globalBounds = null; // 全局坐标范围（固定）
        
        // 播放控制相关属性
        this.isPlaying = false;
        this.isPaused = false;
        this.currentFrameIndex = 0;
        this.playbackDelay = 500; // 默认延时500毫秒
        this.examData = []; // 考试数据
        
        // 参考线显示控制
        this.showReferenceLines = true;
        this.referenceLines = null; // 存储参考线数据
        
        // 去抖动显示控制
        this.showSmoothedSkeleton = true;
        this.smoothedPose = null; // 存储去抖动后的姿势数据
        
        this.ws = null; // 新增：用于存储WebSocket实例
        
        // WebSocket重连控制
        this.reconnectAttempts = 0;
        this.maxReconnectAttempts = 3;
        this.reconnectDelay = 3000;
        this.isReconnecting = false;
        
        this.setupCanvas();
        this.setupEventListeners();
        
        console.log('构造函数完成，使用默认值建立WebSocket连接');
        
        // 直接使用全局变量的默认值建立WebSocket连接
        this.setupWebSocket(personID, taskID);
    }

    setupCanvas() {
        // 设置canvas尺寸
        const resizeCanvas = () => {
            const container = this.canvas.parentElement;
            this.canvas.width = container.clientWidth - 40;
            this.canvas.height = container.clientHeight - 80;
        };

        resizeCanvas();
        window.addEventListener('resize', resizeCanvas);
    }

    setupWebSocket(personID, taskID) {
        console.log(`setupWebSocket被调用: personID="${personID}", taskID="${taskID}"`);
        
        if (this.ws) {
            this.ws.close();
        }
        
        if (!personID || !taskID) {
            console.warn('person_id和task_id不能为空，使用默认值');
            personID = personID || 'testuser';
            taskID = taskID || 'testtask';
        }
        
        const wsUrl = `ws://${window.location.host}/ws?person_id=${encodeURIComponent(personID)}&task_id=${encodeURIComponent(taskID)}`;
        console.log(`建立WebSocket连接: ${wsUrl}`);
        
        this.ws = new WebSocket(wsUrl);
        
        this.ws.onopen = () => {
            this.updateConnectionStatus(true);
            console.log('WebSocket连接已建立');
            // 重置重连计数
            this.reconnectAttempts = 0;
            this.isReconnecting = false;
        };

        this.ws.onmessage = (event) => {
            const data = JSON.parse(event.data);
            if (data.type === 'pose_data') {
                this.handlePoseData(data.data);
            } else if (data.type === 'history') {
                this.handleHistoryData(data.data);
            }
        };

        this.ws.onclose = (event) => {
            this.updateConnectionStatus(false);
            console.log('WebSocket连接已关闭，代码:', event.code, '原因:', event.reason);
            
            // 只有在非主动关闭且未超过最大重连次数时才重连
            if (!this.isReconnecting && this.reconnectAttempts < this.maxReconnectAttempts) {
                this.isReconnecting = true;
                this.reconnectAttempts++;
                console.log(`尝试第${this.reconnectAttempts}次重连...`);
                
                setTimeout(() => {
                    this.setupWebSocket(personID, taskID);
                }, this.reconnectDelay);
            } else if (this.reconnectAttempts >= this.maxReconnectAttempts) {
                console.log('已达到最大重连次数，停止重连');
                this.updateConnectionStatus(false);
            }
        };

        this.ws.onerror = (error) => {
            console.error('WebSocket错误:', error);
            this.updateConnectionStatus(false);
        };
    }

    // 设置事件监听器
    setupEventListeners() {
        // 加载测试数据按钮（现在用于触发文件选择）
        document.getElementById('loadDataBtn').addEventListener('click', () => {
            document.getElementById('fileInput').click();
        });

        // 校准按钮
        document.getElementById('calibrateBtn').addEventListener('click', () => {
            this.startCalibration();
        });

        // 清除数据按钮
        document.getElementById('clearBtn').addEventListener('click', () => {
            this.clearData();
        });

        // 导出数据按钮
        document.getElementById('exportBtn').addEventListener('click', () => {
            this.exportData();
        });

        // 文件上传
        document.getElementById('fileInput').addEventListener('change', (event) => {
            this.handleFileUpload(event.target.files[0]);
        });

        // 显示控制开关
        document.getElementById('showReferenceSkeleton').addEventListener('change', (event) => {
            this.showReferenceSkeleton = event.target.checked;
            this.redrawCanvas();
        });

        document.getElementById('showCurrentSkeleton').addEventListener('change', (event) => {
            this.showCurrentSkeleton = event.target.checked;
            this.redrawCanvas();
        });

        document.getElementById('showReferenceLines').addEventListener('change', (event) => {
            this.showReferenceLines = event.target.checked;
            this.redrawCanvas();
        });

        document.getElementById('showSmoothedSkeleton').addEventListener('change', (event) => {
            this.showSmoothedSkeleton = event.target.checked;
            this.redrawCanvas();
        });

        // 考试按钮
        document.getElementById('examBtn').addEventListener('click', () => {
            this.startExam();
        });

        // 播放控制相关事件监听器
        document.getElementById('pauseBtn').addEventListener('click', () => {
            this.pausePlayback();
        });

        document.getElementById('resumeBtn').addEventListener('click', () => {
            this.resumePlayback();
        });

        document.getElementById('playbackDelay').addEventListener('change', (event) => {
            this.playbackDelay = parseInt(event.target.value);
        });

        // 抖动过滤控制
        document.getElementById('smoothingFactor').addEventListener('input', (event) => {
            const value = parseFloat(event.target.value);
            document.getElementById('smoothingFactorValue').textContent = value.toFixed(1);
            this.updateSmoothingFactor(value);
        });

        document.getElementById('historySize').addEventListener('change', (event) => {
            const value = parseInt(event.target.value);
            this.updateHistorySize(value);
        });

        document.getElementById('clearHistoryBtn').addEventListener('click', () => {
            this.clearHistory();
        });

        // 连接按钮
        document.getElementById('connectBtn').addEventListener('click', () => {
            personID = document.getElementById('personIdInput').value.trim() || 'testuser';
            taskID = document.getElementById('taskIdInput').value.trim() || 'testtask';
            this.setupWebSocket(personID, taskID);
            console.log(`切换到人员ID: ${personID}, 任务ID: ${taskID}`);
        });
    }

    // 更新连接状态
    updateConnectionStatus(connected) {
        const statusElement = document.getElementById('connectionStatus');
        const statusText = document.getElementById('statusText');
        const wsStatus = document.getElementById('wsStatus');

        if (connected) {
            statusElement.className = 'connection-status connected';
            statusText.textContent = '已连接';
            wsStatus.textContent = '已连接';
            wsStatus.className = 'status-value';
        } else {
            statusElement.className = 'connection-status disconnected';
            statusText.textContent = '连接断开';
            wsStatus.textContent = '连接断开';
            wsStatus.className = 'status-value error';
        }
    }

    // 处理姿势数据
    handlePoseData(data) {
        console.log('handlePoseData被调用:', {
            isExamMode: this.isExamMode,
            isCalibrated: this.isCalibrated,
            data: data
        });
        
        // 在考试模式下或校准模式下处理姿势数据
        if (this.isExamMode || this.isCalibrated) {
            console.log('处理姿势数据，开始绘制...');
            this.currentPose = data;
            this.smoothedPose = data; // 默认使用原始数据，后续会更新为去抖动数据
            this.frameCount++;
            this.updateUI(data);
            this.drawPose(data.pose_data);
            console.log('姿势数据绘制完成');
        } else {
            console.log('跳过姿势数据处理：不在考试模式且未校准');
        }
    }

    // 重绘画布
    redrawCanvas() {
        if (this.currentPose) {
            this.drawPose(this.currentPose.pose_data);
        }
    }

    // 更新处理状态
    updateProcessStatus(step, status) {
        const stepElement = document.getElementById(`step${step}`);
        stepElement.className = `status-step ${status}`;
        
        const statusElement = stepElement.querySelector('.step-status');
        switch(status) {
            case 'waiting':
                statusElement.textContent = '等待';
                break;
            case 'active':
                statusElement.textContent = '进行中';
                break;
            case 'completed':
                statusElement.textContent = '完成';
                break;
        }
    }

    // 计算全局坐标范围
    calculateGlobalBounds() {
        if (this.allPoseData.length === 0) return;

        let minX = Infinity, maxX = -Infinity;
        let minY = Infinity, maxY = -Infinity;

        this.allPoseData.forEach(item => {
            if (item.pose_data && item.pose_data.keypoints) {
                item.pose_data.keypoints.forEach(kp => {
                    if (kp.confidence > 0.5) {
                        minX = Math.min(minX, kp.x);
                        maxX = Math.max(maxX, kp.x);
                        minY = Math.min(minY, kp.y);
                        maxY = Math.max(maxY, kp.y);
                    }
                });
            }
        });

        this.globalBounds = { minX, maxX, minY, maxY };
        console.log('全局坐标范围:', this.globalBounds);
    }

    // 绘制校准结果
    drawCalibrationResult() {
        if (!this.referencePose || !this.globalBounds) return;

        const ctx = this.ctx;
        const canvas = this.canvas;
        
        // 清除画布
        ctx.clearRect(0, 0, canvas.width, canvas.height);

        // 设置画布背景
        ctx.fillStyle = 'rgba(0, 0, 0, 0.3)';
        ctx.fillRect(0, 0, canvas.width, canvas.height);

        const { minX, maxX, minY, maxY } = this.globalBounds;
        const padding = 50;
        const poseWidth = maxX - minX;
        const poseHeight = maxY - minY;
        
        // 计算缩放比例，保持宽高比
        const scaleX = (canvas.width - 2 * padding) / poseWidth;
        const scaleY = (canvas.height - 2 * padding) / poseHeight;
        const scale = Math.min(scaleX, scaleY, 2);
        
        // 计算居中偏移
        const scaledWidth = poseWidth * scale;
        const scaledHeight = poseHeight * scale;
        const offsetX = (canvas.width - scaledWidth) / 2;
        const offsetY = (canvas.height - scaledHeight) / 2;

        // 绘制参考骨骼（校准结果）
        this.drawReferenceSkeleton(ctx, minX, minY, scale, offsetX, offsetY);

        // 绘制参考线（校准结果）
        if (this.referenceLines && this.showReferenceLines) {
            this.drawReferenceLines(ctx, minX, minY, scale, offsetX, offsetY);
        }

        // 绘制当前骨骼（如果有的话）
        if (this.currentPose && this.currentPose.pose_data && this.showCurrentSkeleton) {
            this.drawCurrentSkeleton(ctx, this.currentPose.pose_data.keypoints, minX, minY, scale, offsetX, offsetY);
        }

        // 绘制去抖动骨骼（如果有的话）
        if (this.showSmoothedSkeleton && this.smoothedPose && this.smoothedPose.pose_data) {
            this.drawSmoothedSkeleton(ctx, this.smoothedPose.pose_data.keypoints, minX, minY, scale, offsetX, offsetY);
        }

        // 绘制坐标轴
        this.drawAxes(minX, minY, maxX, maxY, scale, offsetX, offsetY);

        // 更新UI显示校准状态
        document.getElementById('calibrationStatus').textContent = '已校准';
        document.getElementById('calibrationStatus').className = 'status-value';
        
        // 显示校准信息
        document.getElementById('overallScore').textContent = '校准完成';
        document.getElementById('bodyLeanForward').textContent = '参考标准';
        document.getElementById('bodyLeanBackward').textContent = '已建立';
        document.getElementById('headTurnLeft').textContent = '前20帧';
        document.getElementById('headTurnRight').textContent = '平均值';
        document.getElementById('headTiltForward').textContent = '蓝色虚线';
        document.getElementById('headTiltBackward').textContent = '参考骨骼';
        document.getElementById('legsStraight').textContent = '已就绪';
        document.getElementById('confidence').textContent = '100%';
    }

    // 处理历史数据
    handleHistoryData(history) {
        // 只有在有导入数据时才处理历史数据
        if (history.length > 0 && this.allPoseData.length > 0) {
            const latestPose = history[history.length - 1];
            this.handlePoseData(latestPose);
        }
    }

    // 更新UI
    updateUI(data) {
        // 更新状态
        document.getElementById('frameCount').textContent = this.frameCount;
        document.getElementById('calibrationStatus').textContent = 
            data.analysis && data.analysis.confidence > 0 ? '已校准' : '未校准';

        // 更新分析结果
        if (data.analysis) {
            document.getElementById('overallScore').textContent = 
                `${data.analysis.overall_score.toFixed(1)}/100`;
            
            // 头部姿态检测
            document.getElementById('headTurnLeft').textContent = 
                `${data.analysis.head_turn_left.toFixed(1)}°`;
            document.getElementById('headTurnRight').textContent = 
                `${data.analysis.head_turn_right.toFixed(1)}°`;
            document.getElementById('headTiltLeft').textContent = 
                `${data.analysis.head_tilt_left.toFixed(1)}°`;
            document.getElementById('headTiltRight').textContent = 
                `${data.analysis.head_tilt_right.toFixed(1)}°`;
            document.getElementById('headTiltForward').textContent = 
                `${data.analysis.head_tilt_forward.toFixed(1)}°`;
            document.getElementById('headTiltBackward').textContent = 
                `${data.analysis.head_tilt_backward.toFixed(1)}°`;
            document.getElementById('headUpward').textContent = 
                `${data.analysis.head_upward.toFixed(1)}°`;
            
            // 肩部姿态检测
            document.getElementById('shoulderLevel').textContent = 
                `${data.analysis.shoulder_level.toFixed(1)}°`;
            document.getElementById('shoulderRetraction').textContent = 
                `${data.analysis.shoulder_retraction.toFixed(1)}°`;
            
            // 身体姿态检测
            document.getElementById('bodyTurnLeft').textContent = 
                `${data.analysis.body_turn_left.toFixed(1)}°`;
            document.getElementById('bodyTurnRight').textContent = 
                `${data.analysis.body_turn_right.toFixed(1)}°`;
            document.getElementById('bodyTiltLeft').textContent = 
                `${data.analysis.body_tilt_left.toFixed(1)}°`;
            document.getElementById('bodyTiltRight').textContent = 
                `${data.analysis.body_tilt_right.toFixed(1)}°`;
            document.getElementById('bodyLeanForward').textContent = 
                `${data.analysis.body_lean_forward.toFixed(1)}°`;
            document.getElementById('bodyLeanBackward').textContent = 
                `${data.analysis.body_lean_backward.toFixed(1)}°`;
            
            // 腿部姿态检测
            document.getElementById('legsStraight').textContent = 
                data.analysis.legs_straight ? '是' : '否';
            
            // 置信度
            document.getElementById('confidence').textContent = 
                `${(data.analysis.confidence * 100).toFixed(1)}%`;
        }
    }

    // 绘制当前姿势
    drawPose(poseData) {
        const ctx = this.ctx;
        const canvas = this.canvas;
        
        // 清除画布
        ctx.clearRect(0, 0, canvas.width, canvas.height);

        // 设置画布背景
        ctx.fillStyle = 'rgba(0, 0, 0, 0.3)';
        ctx.fillRect(0, 0, canvas.width, canvas.height);

        // 计算坐标范围
        let minX, maxX, minY, maxY;
        
        // 如果有全局坐标范围，使用它
        if (this.globalBounds) {
            ({ minX, maxX, minY, maxY } = this.globalBounds);
        } else {
            // 否则使用当前姿势的范围
            minX = Infinity, maxX = -Infinity;
            minY = Infinity, maxY = -Infinity;
            
            const keypoints = poseData.keypoints;
            keypoints.forEach(kp => {
                if (kp.confidence > 0.5) {
                    minX = Math.min(minX, kp.x);
                    maxX = Math.max(maxX, kp.x);
                    minY = Math.min(minY, kp.y);
                    maxY = Math.max(maxY, kp.y);
                }
            });
            
            // 确保有有效范围
            if (minX === Infinity) {
                minX = 0; maxX = 1000; minY = 0; maxY = 1000;
            }
        }
        
        const keypoints = poseData.keypoints;

        const padding = 50;
        const poseWidth = maxX - minX;
        const poseHeight = maxY - minY;
        
        // 计算缩放比例，保持宽高比
        const scaleX = (canvas.width - 2 * padding) / poseWidth;
        const scaleY = (canvas.height - 2 * padding) / poseHeight;
        const scale = Math.min(scaleX, scaleY, 2); // 限制最大缩放
        
        // 计算居中偏移
        const scaledWidth = poseWidth * scale;
        const scaledHeight = poseHeight * scale;
        const offsetX = (canvas.width - scaledWidth) / 2;
        const offsetY = (canvas.height - scaledHeight) / 2;

        // 绘制参考骨骼（如果已校准且开启显示）
        if (this.referencePose && this.showReferenceSkeleton && this.isCalibrated) {
            this.drawReferenceSkeleton(ctx, minX, minY, scale, offsetX, offsetY);
        }

        // 绘制参考线（如果已校准且开启显示）
        if (this.referenceLines && this.showReferenceLines && this.isCalibrated) {
            this.drawReferenceLines(ctx, minX, minY, scale, offsetX, offsetY);
        }

        // 绘制当前骨骼（如果开启显示）
        if (this.showCurrentSkeleton) {
            this.drawCurrentSkeleton(ctx, keypoints, minX, minY, scale, offsetX, offsetY);
        }

        // 绘制去抖动骨骼（如果开启显示且有去抖动数据）
        if (this.showSmoothedSkeleton && this.smoothedPose && this.smoothedPose.pose_data) {
            this.drawSmoothedSkeleton(ctx, this.smoothedPose.pose_data.keypoints, minX, minY, scale, offsetX, offsetY);
        }

        // 绘制坐标轴
        this.drawAxes(minX, minY, maxX, maxY, scale, offsetX, offsetY);
    }

    // 绘制坐标轴
    drawAxes(minX, minY, maxX, maxY, scale, offsetX, offsetY) {
        const ctx = this.ctx;
        const canvas = this.canvas;

        // 设置虚线样式
        ctx.setLineDash([5, 5]);
        ctx.strokeStyle = 'rgba(255, 255, 255, 0.15)';
        ctx.lineWidth = 1;
        ctx.font = '10px Arial';
        ctx.fillStyle = 'rgba(255, 255, 255, 0.4)';

        // 计算坐标轴位置 - 靠近画布边框
        const margin = 30; // 距离边框的边距
        const axisX = margin; // Y轴位置
        const axisY = canvas.height - margin; // X轴位置
        
        // 计算骨骼图的实际显示尺寸
        const poseWidth = maxX - minX;
        const poseHeight = maxY - minY;
        const displayWidth = poseWidth * scale;
        const displayHeight = poseHeight * scale;
        
        // Y轴 - 从画布左侧开始
        ctx.beginPath();
        ctx.moveTo(axisX, margin);
        ctx.lineTo(axisX, axisY);
        ctx.stroke();

        // X轴 - 从画布底部开始
        ctx.beginPath();
        ctx.moveTo(axisX, axisY);
        ctx.lineTo(canvas.width - margin, axisY);
        ctx.stroke();

        // 绘制X轴刻度
        const numTicks = 5;
        for (let i = 0; i <= numTicks; i++) {
            const x = axisX + (canvas.width - 2 * margin) * i / numTicks;
            const y = axisY;
            
            // 刻度线使用实线
            ctx.setLineDash([]);
            ctx.beginPath();
            ctx.moveTo(x, y - 3);
            ctx.lineTo(x, y + 3);
            ctx.stroke();
            
            ctx.textAlign = 'center';
            const value = Math.round(minX + poseWidth * i / numTicks);
            ctx.fillText(value, x, y + 15);
        }

        // 绘制Y轴刻度
        for (let i = 0; i <= numTicks; i++) {
            const x = axisX;
            const y = margin + (axisY - margin) * (1 - i / numTicks);
            
            // 刻度线使用实线
            ctx.setLineDash([]);
            ctx.beginPath();
            ctx.moveTo(x - 3, y);
            ctx.lineTo(x + 3, y);
            ctx.stroke();
            
            ctx.textAlign = 'right';
            const value = Math.round(minY + poseHeight * i / numTicks);
            ctx.fillText(value, x - 8, y + 3);
        }
        
        // 恢复实线样式，避免影响其他绘制
        ctx.setLineDash([]);
    }

    // 绘制参考骨骼
    drawReferenceSkeleton(ctx, minX, minY, scale, offsetX, offsetY) {
        // 绘制参考骨骼 - 使用蓝色虚线
        ctx.setLineDash([8, 4]);
        ctx.strokeStyle = 'rgba(59, 130, 246, 0.6)'; // 蓝色
        ctx.lineWidth = 2;
        ctx.lineCap = 'round';

        POSE_CONNECTIONS.forEach(([start, end]) => {
            const startKp = this.referencePose.keypoints[start];
            const endKp = this.referencePose.keypoints[end];

            if (startKp.confidence > 0.5 && endKp.confidence > 0.5) {
                const x1 = (startKp.x - minX) * scale + offsetX;
                const y1 = (startKp.y - minY) * scale + offsetY;
                const x2 = (endKp.x - minX) * scale + offsetX;
                const y2 = (endKp.y - minY) * scale + offsetY;

                ctx.beginPath();
                ctx.moveTo(x1, y1);
                ctx.lineTo(x2, y2);
                ctx.stroke();
            }
        });

        // 绘制参考关键点 - 使用蓝色半透明
        this.referencePose.keypoints.forEach((kp, index) => {
            if (kp.confidence > 0.5) {
                const x = (kp.x - minX) * scale + offsetX;
                const y = (kp.y - minY) * scale + offsetY;

                ctx.fillStyle = 'rgba(59, 130, 246, 0.4)';
                ctx.strokeStyle = 'rgba(59, 130, 246, 0.8)';

                ctx.beginPath();
                ctx.arc(x, y, 4, 0, 2 * Math.PI);
                ctx.fill();
                ctx.lineWidth = 1;
                ctx.stroke();
            }
        });

        // 恢复实线样式
        ctx.setLineDash([]);
    }

    // 绘制参考线
    drawReferenceLines(ctx, minX, minY, scale, offsetX, offsetY) {
        if (!this.showReferenceLines || !this.referenceLines) {
            return;
        }

        ctx.save();
        ctx.lineWidth = 2;
        ctx.setLineDash([5, 5]); // 虚线效果

        // 绘制水平线
        const horizontalLines = [
            this.referenceLines.eyeLevel,
            this.referenceLines.shoulderLevel,
            this.referenceLines.elbowLevel,
            this.referenceLines.hipLevel,
            this.referenceLines.kneeLevel,
            this.referenceLines.footLevel
        ];

        horizontalLines.forEach(line => {
            if (line && line.y !== undefined) {
                const canvasY = (line.y - minY) * scale + offsetY;
                
                ctx.strokeStyle = line.color;
                ctx.beginPath();
                
                // 计算水平线段的长度（画布宽度的60%）
                const lineLength = ctx.canvas.width * 0.6;
                const startX = (ctx.canvas.width - lineLength) / 2;
                const endX = startX + lineLength;
                
                ctx.moveTo(startX, canvasY);
                ctx.lineTo(endX, canvasY);
                ctx.stroke();

                // 绘制标签 - 优化位置避免被裁剪
                ctx.fillStyle = line.color;
                ctx.font = 'bold 12px Arial';
                ctx.textAlign = 'left';
                
                // 根据位置调整标签显示
                let labelX = startX + 5;
                let labelY = canvasY - 5;
                
                // 如果标签太靠近顶部，调整到下方
                if (labelY < 20) {
                    labelY = canvasY + 15;
                }
                
                // 如果标签太靠近底部，调整到上方
                if (labelY > ctx.canvas.height - 10) {
                    labelY = canvasY - 10;
                }
                
                ctx.fillText(line.label, labelX, labelY);
            }
        });

        // 绘制垂直线
        if (this.referenceLines.centerVertical && this.referenceLines.centerVertical.x !== undefined) {
            const canvasX = (this.referenceLines.centerVertical.x - minX) * scale + offsetX;
            
            ctx.strokeStyle = this.referenceLines.centerVertical.color;
            ctx.beginPath();
            ctx.moveTo(canvasX, 0);
            ctx.lineTo(canvasX, ctx.canvas.height);
            ctx.stroke();

            // 绘制标签 - 优化位置
            ctx.fillStyle = this.referenceLines.centerVertical.color;
            ctx.font = 'bold 12px Arial';
            ctx.textAlign = 'left';
            
            // 根据位置调整标签显示
            let labelX = canvasX + 5;
            let labelY = 20;
            
            // 如果垂直线太靠近右边缘，调整标签位置
            if (canvasX > ctx.canvas.width - 100) {
                labelX = canvasX - 80;
            }
            
            ctx.fillText(this.referenceLines.centerVertical.label, labelX, labelY);
        }

        ctx.restore();
    }

    drawCurrentSkeleton(ctx, keypoints, minX, minY, scale, offsetX, offsetY) {
        // 绘制当前骨骼 - 使用绿色实线
        ctx.strokeStyle = '#4ade80';
        ctx.lineWidth = 3;
        ctx.lineCap = 'round';

        POSE_CONNECTIONS.forEach(([start, end]) => {
            const startKp = keypoints[start];
            const endKp = keypoints[end];

            if (startKp.confidence > 0.5 && endKp.confidence > 0.5) {
                const x1 = (startKp.x - minX) * scale + offsetX;
                const y1 = (startKp.y - minY) * scale + offsetY;
                const x2 = (endKp.x - minX) * scale + offsetX;
                const y2 = (endKp.y - minY) * scale + offsetY;

                ctx.beginPath();
                ctx.moveTo(x1, y1);
                ctx.lineTo(x2, y2);
                ctx.stroke();
            }
        });

        // 绘制当前关键点
        keypoints.forEach((kp, index) => {
            if (kp.confidence > 0.5) {
                const x = (kp.x - minX) * scale + offsetX;
                const y = (kp.y - minY) * scale + offsetY;

                // 根据置信度设置颜色
                const alpha = kp.confidence;
                ctx.fillStyle = `rgba(74, 222, 128, ${alpha})`;
                ctx.strokeStyle = `rgba(255, 255, 255, ${alpha})`;

                // 绘制关键点
                ctx.beginPath();
                ctx.arc(x, y, 6, 0, 2 * Math.PI);
                ctx.fill();
                ctx.lineWidth = 2;
                ctx.stroke();

                // 绘制关键点标签（可选）
                if (index === 0) { // 只显示鼻子标签
                    ctx.fillStyle = 'rgba(255, 255, 255, 0.8)';
                    ctx.font = '12px Arial';
                    ctx.textAlign = 'center';
                    ctx.fillText(KEYPOINT_NAMES[index], x, y - 15);
                }
            }
        });
    }

    // 绘制去抖动骨骼
    drawSmoothedSkeleton(ctx, keypoints, minX, minY, scale, offsetX, offsetY) {
        // 绘制去抖动骨骼 - 使用紫色虚线
        ctx.setLineDash([8, 4]);
        ctx.strokeStyle = 'rgba(147, 51, 234, 0.6)'; // 紫色
        ctx.lineWidth = 2;
        ctx.lineCap = 'round';

        POSE_CONNECTIONS.forEach(([start, end]) => {
            const startKp = keypoints[start];
            const endKp = keypoints[end];

            if (startKp.confidence > 0.5 && endKp.confidence > 0.5) {
                const x1 = (startKp.x - minX) * scale + offsetX;
                const y1 = (startKp.y - minY) * scale + offsetY;
                const x2 = (endKp.x - minX) * scale + offsetX;
                const y2 = (endKp.y - minY) * scale + offsetY;

                ctx.beginPath();
                ctx.moveTo(x1, y1);
                ctx.lineTo(x2, y2);
                ctx.stroke();
            }
        });

        // 绘制去抖动关键点 - 使用紫色半透明
        keypoints.forEach((kp, index) => {
            if (kp.confidence > 0.5) {
                const x = (kp.x - minX) * scale + offsetX;
                const y = (kp.y - minY) * scale + offsetY;

                ctx.fillStyle = 'rgba(147, 51, 234, 0.4)';
                ctx.strokeStyle = 'rgba(147, 51, 234, 0.8)';

                ctx.beginPath();
                ctx.arc(x, y, 4, 0, 2 * Math.PI);
                ctx.fill();
                ctx.lineWidth = 1;
                ctx.stroke();
            }
        });

        // 恢复实线样式
        ctx.setLineDash([]);
    }

    // 计算平均姿势
    calculateAveragePose(poseDataArray) {
        if (poseDataArray.length === 0) return null;

        const numKeypoints = poseDataArray[0].keypoints.length;
        const averageKeypoints = [];

        // 计算每个关键点的平均值
        for (let i = 0; i < numKeypoints; i++) {
            let sumX = 0, sumY = 0, sumConfidence = 0;
            let validCount = 0;

            poseDataArray.forEach(poseData => {
                const kp = poseData.keypoints[i];
                if (kp.confidence > 0.5) {
                    sumX += kp.x;
                    sumY += kp.y;
                    sumConfidence += kp.confidence;
                    validCount++;
                }
            });

            if (validCount > 0) {
                averageKeypoints.push({
                    x: sumX / validCount,
                    y: sumY / validCount,
                    confidence: sumConfidence / validCount
                });
            } else {
                averageKeypoints.push({
                    x: 0,
                    y: 0,
                    confidence: 0
                });
            }
        }

        return {
            keypoints: averageKeypoints,
            timestamp: Date.now(),
            frame_id: -1
        };
    }

    // 计算参考线
    calculateReferenceLines(poseData) {
        if (!poseData || !poseData.keypoints) {
            return null;
        }

        const keypoints = poseData.keypoints;
        
        // 获取关键点坐标
        const nose = keypoints[0];      // 鼻子
        const leftEye = keypoints[1];   // 左眼
        const rightEye = keypoints[2];  // 右眼
        const leftShoulder = keypoints[5];  // 左肩
        const rightShoulder = keypoints[6]; // 右肩
        const leftHip = keypoints[11];      // 左髋
        const rightHip = keypoints[12];     // 右髋
        const leftAnkle = keypoints[15];    // 左踝
        const rightAnkle = keypoints[16];   // 右踝

        // 计算参考线
        const referenceLines = {
            // 眼睛水平线（两眼中心）
            eyeLevel: {
                y: (leftEye.y + rightEye.y) / 2,
                color: 'rgba(255, 107, 107, 0.6)', // 浅红色
                label: '眼睛水平线'
            },
            // 肩膀水平线（两肩中心）
            shoulderLevel: {
                y: (leftShoulder.y + rightShoulder.y) / 2,
                color: 'rgba(78, 205, 196, 0.6)', // 浅青色
                label: '肩膀水平线'
            },
            // 肘部水平线（两肘中心）
            elbowLevel: {
                y: (keypoints[7].y + keypoints[8].y) / 2, // 左肘和右肘
                color: 'rgba(255, 159, 64, 0.6)', // 浅橙色
                label: '肘部水平线'
            },
            // 腰部水平线（两髋中心）
            hipLevel: {
                y: (leftHip.y + rightHip.y) / 2,
                color: 'rgba(69, 183, 209, 0.6)', // 浅蓝色
                label: '腰部水平线'
            },
            // 膝部水平线（两膝中心）
            kneeLevel: {
                y: (keypoints[13].y + keypoints[14].y) / 2, // 左膝和右膝
                color: 'rgba(150, 206, 180, 0.6)', // 浅绿色
                label: '膝部水平线'
            },
            // 脚底水平线（脚踝下方约10%身高）
            footLevel: {
                y: Math.max(leftAnkle.y, rightAnkle.y) + (Math.max(leftAnkle.y, rightAnkle.y) - Math.min(leftEye.y, rightEye.y)) * 0.1,
                color: 'rgba(255, 234, 167, 0.6)', // 浅黄色
                label: '脚底水平线'
            },
            // 两眼中间位置垂直线
            centerVertical: {
                x: (leftEye.x + rightEye.x) / 2,
                color: 'rgba(221, 160, 221, 0.6)', // 浅紫色
                label: '中心垂直线'
            }
        };

        return referenceLines;
    }

    // 开始校准
    async startCalibration() {
        try {
            this.showLoading(true);
            this.updateProcessStatus(2, 'active');
            
            if (this.allPoseData.length < 20) {
                throw new Error('需要至少20帧数据进行校准');
            }

            // 使用前20帧进行校准（固定参考标准）
            const calibrationData = this.allPoseData.slice(0, 20).map(item => item.pose_data);
            
            // 创建AbortController用于超时控制
            const controller = new AbortController();
            const timeoutId = setTimeout(() => controller.abort(), 5000); // 5秒超时
            
            try {
                const calibrateResponse = await fetch('/api/calibrate', {
                    method: 'POST',
                    headers: {
                        'Content-Type': 'application/json',
                    },
                    body: JSON.stringify({
                        person_id: personID,
                        pose_data: calibrationData
                    }),
                    signal: controller.signal
                });
                
                clearTimeout(timeoutId); // 清除超时定时器
                
                if (!calibrateResponse.ok) {
                    throw new Error(`校准失败: ${calibrateResponse.status} ${calibrateResponse.statusText}`);
                }
                
                const result = await calibrateResponse.json();
                console.log('校准成功:', result);
                
                // 保存参考骨骼数据（使用校准数据的平均值）
                if (calibrationData.length > 0) {
                    // 计算平均姿势
                    this.referencePose = this.calculateAveragePose(calibrationData);
                    // 计算参考线
                    this.referenceLines = this.calculateReferenceLines(this.referencePose);
                }
                
                this.isCalibrated = true;
                document.getElementById('calibrationStatus').textContent = '已校准';
                document.getElementById('calibrationStatus').className = 'status-value';
                
                // 启用考试按钮
                document.getElementById('examBtn').disabled = false;
                
                this.updateProcessStatus(2, 'completed');
                this.showLoading(false);
                
                // 立即绘制校准骨骼数据
                if (this.referencePose) {
                    this.drawCalibrationResult();
                }
                
                // 如果有考试数据，显示第一帧
                if (this.allPoseData.length > 20) {
                    const firstExamFrame = this.allPoseData[20];
                    if (firstExamFrame && firstExamFrame.pose_data) {
                        this.currentPose = {
                            pose_data: firstExamFrame.pose_data,
                            analysis: null,
                            timestamp: firstExamFrame.pose_data.timestamp,
                            frame_id: firstExamFrame.pose_data.frame_id
                        };
                        this.drawPose(firstExamFrame.pose_data);
                        console.log('显示第一帧考试数据');
                    }
                }
                
                console.log('校准完成！已使用前20帧建立个人化参考标准。');
            } catch (error) {
                clearTimeout(timeoutId); // 清除超时定时器
                
                if (error.name === 'AbortError') {
                    console.error('校准请求超时（5秒）');
                    alert('校准请求超时，请检查服务器状态或重试');
                } else {
                    console.error('校准失败:', error);
                    alert('校准失败: ' + error.message);
                }
                
                this.updateProcessStatus(2, 'waiting');
                this.showLoading(false);
            }
        } catch (error) {
            console.error('校准失败:', error);
            this.updateProcessStatus(2, 'waiting');
            this.showLoading(false);
        }
    }

    // 开始考试
    async startExam() {
        try {
            this.showLoading(true);
            this.updateProcessStatus(3, 'active');
            
            if (!this.isCalibrated) {
                throw new Error('请先完成校准');
            }
            
            if (this.allPoseData.length < 21) {
                throw new Error('需要至少21帧数据进行考试');
            }
            
            this.isExamMode = true;
            this.frameCount = 0;
            this.currentFrameIndex = 0;
            this.isPlaying = true;
            this.isPaused = false;
            
            // 准备考试数据（第21帧开始）
            this.examData = this.allPoseData.slice(20);
            console.log(`开始考试，共 ${this.examData.length} 帧数据`);
            
            // 更新UI显示
            this.updatePlaybackUI();
            
            // 开始播放
            this.playNextFrame();
            
        } catch (error) {
            console.error('考试失败:', error);
            this.updateProcessStatus(3, 'waiting');
            this.showLoading(false);
        }
    }

    // 播放下一帧
    async playNextFrame() {
        if (!this.isPlaying || this.isPaused) {
            return;
        }

        if (this.currentFrameIndex >= this.examData.length) {
            // 播放完成
            this.isPlaying = false;
            this.updateProcessStatus(3, 'completed');
            this.showLoading(false);
            this.updatePlaybackUI();
            console.log(`考试完成！共处理 ${this.examData.length} 帧考试数据。`);
            return;
        }

        const poseData = this.examData[this.currentFrameIndex];
        console.log(`播放第 ${this.currentFrameIndex + 1} 帧数据:`, poseData);
        
        if (poseData.pose_data) {
            console.log('发送姿势数据到服务器...');
            // 发送到服务器进行分析
            const result = await this.sendPoseData(poseData.pose_data);
            console.log('服务器返回结果:', result);
            
            // 更新当前显示的姿势
            this.currentPose = {
                pose_data: poseData.pose_data,
                analysis: result.analysis,
                timestamp: poseData.pose_data.timestamp,
                frame_id: poseData.pose_data.frame_id
            };
            
            // 更新去抖动数据
            if (result.smoothed_pose) {
                this.smoothedPose = {
                    pose_data: result.smoothed_pose,
                    analysis: result.analysis,
                    timestamp: result.smoothed_pose.timestamp,
                    frame_id: result.smoothed_pose.frame_id
                };
            }
            
            this.frameCount++;
            this.updateUI(this.currentPose);
            console.log('开始绘制姿势数据...');
            this.drawPose(poseData.pose_data);
            console.log('姿势数据绘制完成');
            
            // 更新播放进度
            this.currentFrameIndex++;
            this.updatePlaybackUI();
            
            // 延迟后播放下一帧
            setTimeout(() => {
                this.playNextFrame();
            }, this.playbackDelay);
        } else {
            console.log('跳过无效帧');
            // 跳过无效帧
            this.currentFrameIndex++;
            this.playNextFrame();
        }
    }

    // 暂停播放
    pausePlayback() {
        this.isPaused = true;
        this.updatePlaybackUI();
        console.log('播放已暂停');
    }

    // 继续播放
    resumePlayback() {
        this.isPaused = false;
        this.updatePlaybackUI();
        this.playNextFrame();
        console.log('播放已继续');
    }

    // 更新播放控制UI
    updatePlaybackUI() {
        const pauseBtn = document.getElementById('pauseBtn');
        const resumeBtn = document.getElementById('resumeBtn');
        const currentFrame = document.getElementById('currentFrame');
        const totalFrames = document.getElementById('totalFrames');

        // 更新帧数显示
        currentFrame.textContent = this.currentFrameIndex;
        totalFrames.textContent = this.examData.length;

        // 更新按钮显示
        if (this.isPlaying && !this.isPaused) {
            pauseBtn.style.display = 'inline-block';
            resumeBtn.style.display = 'none';
        } else if (this.isPlaying && this.isPaused) {
            pauseBtn.style.display = 'none';
            resumeBtn.style.display = 'inline-block';
        } else {
            pauseBtn.style.display = 'none';
            resumeBtn.style.display = 'none';
        }
    }

    // 清除数据
    clearData() {
        this.currentPose = null;
        this.referencePose = null;
        this.frameCount = 0;
        this.isCalibrated = false;
        this.isExamMode = false;
        this.allPoseData = [];
        this.globalBounds = null;
        
        // 重置播放控制状态
        this.isPlaying = false;
        this.isPaused = false;
        this.currentFrameIndex = 0;
        this.examData = [];
        
        // 重置参考线
        this.referenceLines = null;
        
        // 重置去抖动状态
        this.smoothedPose = null;
        
        // 清除画布
        const ctx = this.ctx;
        const canvas = this.canvas;
        ctx.clearRect(0, 0, canvas.width, canvas.height);
        
        // 重置UI
        document.getElementById('frameCount').textContent = '0';
        document.getElementById('calibrationStatus').textContent = '未校准';
        document.getElementById('overallScore').textContent = '--';
        document.getElementById('bodyLeanForward').textContent = '--';
        document.getElementById('bodyLeanBackward').textContent = '--';
        document.getElementById('headTurnLeft').textContent = '--';
        document.getElementById('headTurnRight').textContent = '--';
        document.getElementById('headTiltForward').textContent = '--';
        document.getElementById('headTiltBackward').textContent = '--';
        document.getElementById('legsStraight').textContent = '--';
        document.getElementById('confidence').textContent = '--';
        
        // 重置播放控制UI
        document.getElementById('currentFrame').textContent = '--';
        document.getElementById('totalFrames').textContent = '--';
        document.getElementById('pauseBtn').style.display = 'none';
        document.getElementById('resumeBtn').style.display = 'none';
        
        // 重置按钮状态
        document.getElementById('calibrateBtn').disabled = true;
        document.getElementById('examBtn').disabled = true;
        
        // 重置处理状态
        this.updateProcessStatus(1, 'waiting');
        this.updateProcessStatus(2, 'waiting');
        this.updateProcessStatus(3, 'waiting');
        
        console.log('数据已清除');
    }

    // 导出数据
    async exportData() {
        try {
            const response = await fetch(`/api/history?task_id=${encodeURIComponent(taskID)}`);
            const data = await response.json();
            
            const blob = new Blob([JSON.stringify(data.history, null, 2)], {
                type: 'application/json'
            });
            
            const url = URL.createObjectURL(blob);
            const a = document.createElement('a');
            a.href = url;
            a.download = `pose_data_${new Date().toISOString().slice(0, 19).replace(/:/g, '-')}.json`;
            document.body.appendChild(a);
            a.click();
            document.body.removeChild(a);
            URL.revokeObjectURL(url);
            
            console.log('数据导出成功');
        } catch (error) {
            console.error('数据导出失败:', error);
            alert('数据导出失败: ' + error.message);
        }
    }

    // 处理文件上传
    async handleFileUpload(file) {
        if (!file) return;

        try {
            this.showLoading(true);
            this.updateProcessStatus(1, 'active');
            
            const text = await file.text();
            const data = JSON.parse(text);
            
            // 存储所有数据
            this.allPoseData = [];
            if (Array.isArray(data)) {
                this.allPoseData = data;
            } else if (data.pose_data) {
                this.allPoseData = [data];
            }
            
            // 计算全局坐标范围（固定）
            this.calculateGlobalBounds();
            
            console.log(`成功导入 ${this.allPoseData.length} 帧数据`);
            
            // 启用校准按钮
            document.getElementById('calibrateBtn').disabled = false;
            
            // 更新总帧数显示
            document.getElementById('totalFrames').textContent = this.allPoseData.length;
            
            // 更新处理状态
            this.updateProcessStatus(1, 'completed');
            this.showLoading(false);
            
            console.log(`成功导入 ${this.allPoseData.length} 帧测试数据！`);
        } catch (error) {
            console.error('文件上传失败:', error);
            this.updateProcessStatus(1, 'waiting');
            this.showLoading(false);
        }
    }

    // 发送姿势数据
    async sendPoseData(poseData) {
        try {
            const response = await fetch('/api/pose', {
                method: 'POST',
                headers: {
                    'Content-Type': 'application/json',
                },
                body: JSON.stringify({
                    person_id: personID,
                    task_id: taskID,
                    pose_data: poseData
                })
            });

            if (!response.ok) {
                throw new Error('发送姿势数据失败');
            }

            const result = await response.json();
            
            // 如果返回了去抖动后的数据，更新显示
            if (result.smoothed_pose) {
                this.smoothedPose = {
                    pose_data: result.smoothed_pose,
                    analysis: result.analysis,
                    timestamp: result.smoothed_pose.timestamp,
                    frame_id: result.smoothed_pose.frame_id
                };
            }
            
            return result;
        } catch (error) {
            console.error('发送姿势数据失败:', error);
            throw error;
        }
    }

    // 显示加载状态
    showLoading(show) {
        const loading = document.getElementById('loading');
        loading.style.display = show ? 'block' : 'none';
    }

    // 抖动过滤控制方法
    async updateSmoothingFactor(factor) {
        try {
            const response = await fetch('/api/v1/filter/smoothing-factor', {
                method: 'POST',
                headers: {
                    'Content-Type': 'application/json',
                },
                body: JSON.stringify({ factor: factor })
            });

            if (response.ok) {
                console.log(`平滑因子已更新为: ${factor}`);
            }
        } catch (error) {
            console.error('更新平滑因子失败:', error);
        }
    }

    async updateHistorySize(size) {
        try {
            const response = await fetch('/api/v1/filter/history-size', {
                method: 'POST',
                headers: {
                    'Content-Type': 'application/json',
                },
                body: JSON.stringify({ size: size })
            });

            if (response.ok) {
                console.log(`历史帧数已更新为: ${size}`);
            }
        } catch (error) {
            console.error('更新历史帧数失败:', error);
        }
    }

    async clearHistory() {
        try {
            const response = await fetch('/api/v1/filter/clear-history', {
                method: 'POST',
                headers: {
                    'Content-Type': 'application/json',
                },
                body: JSON.stringify({})
            });

            if (response.ok) {
                console.log('历史记录已清除');
            }
        } catch (error) {
            console.error('清除历史记录失败:', error);
        }
    }
}

// 初始化应用
document.addEventListener('DOMContentLoaded', () => {
    new PoseVisualizer();
}); 